#include "ISA.h"

#include <stdio.h>
#include <stdlib.h>
#include <iostream>

using namespace std;

CISA::CISA()
{
	header = ".align 2\n .global main\n .type main, \%function\n main:\n";
	footer = " .size main, .-main \n.ident \"GCC: (GNU) 4.0.1\" \n";
	//Add instructions supported in the ISA here:
    addInstructionType ("STR", 2, REG, MEM, NA);
    //addInstructionType ("LDR", 2, REG, MEM, NA);
	addInstructionType ("MOV", 2, REG, REGIMM, NA);
	addInstructionType ("ADD", 3, REG, REG, REGIMM);
	addInstructionType ("ADC", 3, REG, REG, REGIMM);
	addInstructionType ("SUB", 3, REG, REG, REGIMM);
	addInstructionType ("SBC", 3, REG, REG, REGIMM);
	addInstructionType ("ORR", 3, REG, REG, REGIMM);

	addInstructionType ("CMP", 2, REG, REGIMM, NA);
	addInstructionType ("AND", 3, REG, REG, REGIMM);
	addInstructionType ("EOR", 3, REG, REG, REGIMM);
	addInstructionType ("ORR", 3, REG, REG, REGIMM);
	addInstructionType ("MVN", 2, REG, REGIMM, NA);

	addInstructionType("B", BRANCHINSTRUCTION, NA, NA, NA);

}

CISA::~CISA()
{
	std::vector<CInstruction*>::iterator iter;
	for(iter = instructionTypes.begin(); iter != instructionTypes.end(); ++iter)
		delete *iter;
}

CInstruction* CISA::requestNewInstruction(SGlobalInfo &info)
{
	CInstruction* newInstr;

	unsigned int newInstrIndex = (unsigned int)rand() % instructionTypes.size();
	//cout << "Random Number returned: " << newInstrIndex << endl;
	newInstr = instructionTypes[newInstrIndex]->copy();
	newInstr->GetRandomOperands(info);


	return newInstr;
}

void CISA::addInstructionType(std::string name, unsigned char argNo, opType op1, opType op2, opType op3)
{
	CInstruction* newInstr;
	switch(argNo)
	{
		case 1:
			newInstr = new CInstruction1arg(NULL, op1);
			break;
		case 2:
			newInstr = new CInstruction2args(NULL, op1, op2);
			break;
		case 3:
			newInstr = new CInstruction3args(NULL, op1, op2, op3);
			break;
		case BRANCHINSTRUCTION:
			newInstr = new CInstructionBranch(NULL);
			break;
		default:
			cout << "Illegal number of arguments for instruction " << name << endl;
			return;
	}
	newInstr->name = name;

	instructionTypes.push_back(newInstr);
	return;
}
